package cz.muni.fi.pa165.sme.dao.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;

import cz.muni.fi.pa165.sme.dao.api.BurnedCaloriesDao;
import cz.muni.fi.pa165.sme.dao.entity.BurnedCalories;
import cz.muni.fi.pa165.sme.dao.entity.SportActivity;
import cz.muni.fi.pa165.sme.dao.exception.DataStorageOperationException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import static cz.muni.fi.pa165.sme.common.EntityValidator.validateDeleteEntity;
import static cz.muni.fi.pa165.sme.common.EntityValidator.validateEntityManager;
import static cz.muni.fi.pa165.sme.common.EntityValidator.validateGetEntity;
import static cz.muni.fi.pa165.sme.common.EntityValidator.validateNewEntity;
import static cz.muni.fi.pa165.sme.common.EntityValidator.validateUpdatedEntity;

/**
 *
 * @author Amruz
 */
@Repository
public class BurnedCaloriesDaoImpl implements BurnedCaloriesDao {

	@PersistenceContext
	private EntityManager entityManager;

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	@Override
	@Transactional
	public void createBurnedCalories(final BurnedCalories burnedCalories) {
		validateEntityManager(entityManager);
		validateNewEntity(burnedCalories);

		try{
			entityManager.persist(burnedCalories);
		}catch (PersistenceException e){
			throw new DataStorageOperationException("An error has occurred while inserting a new burned calories record " +
					"object to the database",e);
		}
		entityManager.flush();
	}

	@Override
    @Transactional
	public void deleteBurnedCalories(final BurnedCalories burnedCalories) {
		validateEntityManager(entityManager);
        validateDeleteEntity(burnedCalories);

		try{
			entityManager.remove(entityManager.find(BurnedCalories.class, burnedCalories.getId()));
		}catch (PersistenceException e){
			throw new DataStorageOperationException("An error has occurred while deleting burned calories record from " +
					"the database",e);
		} catch (IllegalArgumentException e){
			throw new DataStorageOperationException("Burned calories record: " + burnedCalories + " does not exist " +
					"in the database, or the argument is invalid",e);
		}
	}

	@Override
//    @Cascade(CascadeType.SAVE_UPDATE)
    @Transactional
	public void updateBurnedCalories(final BurnedCalories burnedCalories) {
		validateEntityManager(entityManager);
		validateUpdatedEntity(burnedCalories);

		try{
			final BurnedCalories oldRecord = entityManager.find(BurnedCalories.class, burnedCalories.getId());
			if(oldRecord == null) {
				throw new IllegalArgumentException("Entity cannot be updated if it does not exist.");
			}

			entityManager.merge(burnedCalories);
		} catch (PersistenceException e){
			throw new DataStorageOperationException("An error has occurred while updating burned calories record in database",e);
		} catch (IllegalArgumentException e){
			throw new DataStorageOperationException("Burned calories record: " + burnedCalories + "does not exist in the database," +
					" or the argument is wrong",e);
		}
	}

	@Override
	@Transactional
	public BurnedCalories findBurnedCalories(final Long id) {
		validateGetEntity(id);
		validateEntityManager(entityManager);

		try{
			return entityManager.find(BurnedCalories.class, id);
		} catch (PersistenceException ex) {
			throw new DataStorageOperationException("An error has occured when looking for a burned activity record in database.", ex);
		}
	}

	@Override
	@Transactional
	public List<BurnedCalories> findBurnedCaloriesHigherThenCalories(final Long calories) {
		validateEntityManager(entityManager);

		try{
			TypedQuery<BurnedCalories> selectHigherThan = entityManager.createNamedQuery(BurnedCalories.SELECT_HIGHER_THAN, BurnedCalories.class);
			selectHigherThan.setParameter("cals", calories);
			return selectHigherThan.getResultList();
		} catch (PersistenceException ex) {
			throw new DataStorageOperationException("An error has occured when looking for a burned activity record in database.", ex);
		}
	}

	@Override
	@Transactional
	public List<BurnedCalories> findAllBurnedCaloriesBySportActivity(final SportActivity sportActivity) {
		if(sportActivity == null || sportActivity.getId() == null) {
			throw new IllegalArgumentException("Sport activity and/or its ID cannot be null.");
		}

		validateEntityManager(entityManager);

		try{
			TypedQuery<BurnedCalories> selectBySportActivity = entityManager.createNamedQuery(BurnedCalories.SELECT_BY_SPORT_ACTIVITY, BurnedCalories.class);
			selectBySportActivity.setParameter("sportActivityId", sportActivity.getId());
			return selectBySportActivity.getResultList();
		} catch (PersistenceException ex) {
			throw new DataStorageOperationException("An error has occured when looking for a burned activity record in database.", ex);
		}
	}


	@Override
	@Transactional
	public List<BurnedCalories> findAllBurnedCalories() {
		validateEntityManager(entityManager);

		try{
			TypedQuery<BurnedCalories> selectByUser = entityManager.createNamedQuery(BurnedCalories.SELECT_ALL, BurnedCalories.class);
			return selectByUser.getResultList();
		} catch (PersistenceException ex) {
			throw new DataStorageOperationException("An error has occured when looking for a burned activity record in database.", ex);
		}
	}
}
